﻿using System.Globalization;
using System.Windows;
using System.Windows.Media;

namespace DrawCurveDemo
{
	public class CurveDrawingVisual : FrameworkElement
	{
		private readonly DrawingVisual drawingVisual;
		private readonly VisualCollection visuals;

		private readonly double curveThickness = 1.5;
		private readonly double primaryThickness = 1.2;
		private readonly double secondaryThickness = 0.8;

		private readonly int maxVal = 240;
		private readonly int minVal = 30;
		private readonly int topVal = 10;
		private readonly int bottomVal = 25;

		private readonly int timeGap = 2;//参考时间间隔
		private readonly int xGap = 20;//竖线间隔

		private readonly int fhr2Offset = -20;//心率曲线2偏移
		private readonly int fhr3Offset = 20;//心率曲线2偏移

		private double offset;//偏移值

		private readonly int direction = 0;//走纸方向（ 0：从右开始  1：从左开始）

		public string fhr1Brush = "#009C26";
		public string fhr2Brush = "#8080C0";
		public string fhr3Brush = "#000000";

		public string primaryBrush = "#DADADA";
		public string secondaryBrush = "#E8E8E8";

		private Pen fhr1Pen;
		private Pen fhr2Pen;
		private Pen fhr3Pen;

		private Pen primaryPen;
		private Pen secondaryPen;

		private SolidColorBrush gridBack;//背景色


		public double Xscale { get; set; } = 1;//横向缩放比例
		public Dictionary<int, FhrModel> DicFhrData { get; set; } = new();
		public int? LastIndex { get; set; }//最新时间索引
		public DateTime? StartTime { get; set; } = DateTime.Now;//开始时间


		public CurveDrawingVisual()
		{
			drawingVisual = new DrawingVisual();
			visuals = new VisualCollection(this) { drawingVisual };

			Init();
			InvalidateVisual();
		}

		private void Init()
		{
			gridBack = Brushes.White;

			fhr1Pen = new Pen(BrushUtil.ToBrush(fhr1Brush), curveThickness);
			fhr2Pen = new Pen(BrushUtil.ToBrush(fhr2Brush), curveThickness);
			fhr3Pen = new Pen(BrushUtil.ToBrush(fhr3Brush), curveThickness);

			primaryPen = new Pen(BrushUtil.ToBrush(primaryBrush), primaryThickness);
			secondaryPen = new Pen(BrushUtil.ToBrush(secondaryBrush), secondaryThickness);

			//冻结画笔，提高性能
			fhr1Pen.Freeze();
			fhr2Pen.Freeze();
			fhr3Pen.Freeze();

			primaryPen.Freeze();
			secondaryPen.Freeze();
		}

		public void UpdateOffset(double offset)
		{
			this.offset = offset;
			InvalidateVisual();
		}

		private void DrawContent(DrawingContext dc)
		{
			var height = RenderSize.Height;
			var width = RenderSize.Width;

			var y_scale = (height - bottomVal - topVal) / (maxVal - minVal);

			//底色
			dc.DrawRectangle(gridBack, null, new Rect(0, 0, width, height));

			Point point1;
			Point point2;

			//横线
			for (int y = minVal; y <= maxVal; y += 10)
			{
				var h = height - (y - minVal) * y_scale - bottomVal;
				point1 = new Point(0, h);
				point2 = new Point(width, h);
				if (y % 30 == 0)
				{
					dc.DrawLine(primaryPen, point1, point2);
					continue;
				}
				dc.DrawLine(secondaryPen, point1, point2);
			}

			var x_offset = offset / Xscale;//曲线零点与绘图零点偏移量
			var origin_offset = x_offset;
			var width_scale = width / Xscale;
			if (LastIndex.HasValue && width_scale > LastIndex.Value / 2)//曲线长度小于视图宽度时的偏移量
			{
				if (direction == 0)
				{
					origin_offset = (float)width_scale - LastIndex.Value / 2;
					x_offset = 120 * timeGap - origin_offset % (120 * timeGap);//单个时间区间Y轴与绘图零点偏移量
					origin_offset = -origin_offset;
				}
				else
				{
					x_offset = 0;
					origin_offset = 0;
				}
			}

#pragma warning disable CS0618

			//竖线与纵坐标刻度文字
			var max_index = width_scale + x_offset;
			for (int i = 0; i < max_index; i += xGap * 2)
			{
				if (i < x_offset) continue;

				var w = (i - x_offset) * Xscale;

				if ((i + (60 * timeGap)) % (60 * timeGap * 2) == 0)
				{
					//胎心刻度值
					for (int y = minVal; y <= maxVal; y += 30)
					{
						var text = new FormattedText(y + "", CultureInfo.CurrentCulture,
							FlowDirection.LeftToRight, new Typeface("Verdana"), 9, Brushes.Black);
						dc.DrawText(text, new Point(w, height - (y - minVal) * y_scale - bottomVal - text.Height));
					}
				}

				point1 = new Point(w, topVal);
				point2 = new Point(w, height - bottomVal);

				if (i % 120 == 0)
				{
					dc.DrawLine(primaryPen, point1, point2);
					continue;
				}
				dc.DrawLine(secondaryPen, point1, point2);
			}

			var start = origin_offset < 0 ? 0 : (int)origin_offset;
			var maxIndex = start * 2 + width_scale * 2;//可视区域最多绘制点数

			for (int i = start * 2, left = start; i <= maxIndex && i <= LastIndex; i += 2, left++)
			{
				//横坐标时间文字
				if (i % 4 == 0)
				{
					if (StartTime.HasValue)
					{
						int seconds = i / 4;
						if (seconds % (60 * timeGap) == 0)
						{
							string time = seconds % (120 * timeGap) == 0 ?
								StartTime.Value.AddSeconds(seconds).ToString("HH:mm") : $"{seconds / 60}分";

							var text = new FormattedText(time, CultureInfo.CurrentCulture,
								FlowDirection.LeftToRight, new Typeface("Verdana"), 11, Brushes.Black);
							if (seconds != 0)
								dc.DrawText(text, new Point((left - origin_offset) * Xscale - text.Width / 2, height - bottomVal));
							else
								dc.DrawText(text, new Point((left - origin_offset) * Xscale, height - bottomVal));
						}
					}
				}
			}

#pragma warning restore CS0618

			FhrModel fhrModel_2 = null;
			for (int i = start * 2, left = start; i <= maxIndex && i <= LastIndex; i += 2, left++)
			{
				DicFhrData.TryGetValue(i, out FhrModel fhrModel_1);

				if (fhrModel_1 != null && fhrModel_2 != null)
				{
					var point1_x = (left - origin_offset) * Xscale;
					var point2_x = (left - 1 - origin_offset) * Xscale;

					if (CheckFhrData(fhrModel_1.Fhr1, fhrModel_2.Fhr1, 1))
					{
						point1 = new Point(point1_x, height - (fhrModel_1.Fhr1.Value - minVal) * y_scale - bottomVal);
						point2 = new Point(point2_x, height - (fhrModel_2.Fhr1.Value - minVal) * y_scale - bottomVal);

						dc.DrawLine(fhr1Pen, point1, point2);
					}

					if (CheckFhrData(fhrModel_1.Fhr2, fhrModel_2.Fhr2, 2))
					{
						point1 = new Point(point1_x, height - (fhrModel_1.Fhr2.Value + fhr2Offset - minVal) * y_scale - bottomVal);
						point2 = new Point(point2_x, height - (fhrModel_2.Fhr2.Value + fhr2Offset - minVal) * y_scale - bottomVal);

						dc.DrawLine(fhr2Pen, point1, point2);
					}

					if (CheckFhrData(fhrModel_1.Fhr3, fhrModel_2.Fhr3, 3))
					{
						point1 = new Point(point1_x, height - (fhrModel_1.Fhr2.Value + fhr3Offset - minVal) * y_scale - bottomVal);
						point2 = new Point(point2_x, height - (fhrModel_2.Fhr2.Value + fhr3Offset - minVal) * y_scale - bottomVal);

						dc.DrawLine(fhr3Pen, point1, point2);
					}
				}

				fhrModel_2 = fhrModel_1;
			}
		}

		//判断胎心率是否有效
		private bool CheckFhrData(int? data1, int? data2, int fetal)
		{
			double offset = 0;
			if (fetal == 2) offset = fhr2Offset;
			if (fetal == 3) offset = fhr3Offset;

			if (data1.HasValue && data2.HasValue)
			{
				if (data1 + offset >= minVal && data1 + offset <= maxVal
					&& data2 + offset >= minVal && data2 + offset <= maxVal
					&& Math.Abs(data1.Value - data2.Value) < 30)
				{
					return true;
				}
			}
			return false;
		}

		protected override int VisualChildrenCount => visuals.Count;
		protected override Visual GetVisualChild(int index)
		{
			return visuals[index];
		}

		protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
		{
			InvalidateVisual();
			base.OnRenderSizeChanged(sizeInfo);
		}

		protected override void OnRender(DrawingContext drawingContext)
		{
			DrawContent(drawingContext);

			base.OnRender(drawingContext);
		}
	}
}
